/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain (a) copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry.ohos.core;

import ohos.app.Context;
import ohos.dcall.CallStateObserver;
import ohos.dcall.DistributedCallManager;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;

import java.io.Closeable;
import java.io.IOException;

import io.sentry.Breadcrumb;
import io.sentry.IHub;
import io.sentry.Integration;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.ohos.core.util.Permissions;
import io.sentry.util.Objects;

/**
 * PhoneStateBreadcrumbsIntegration
 *
 * @since 2021-06-01
 */
public final class PhoneStateBreadcrumbsIntegration implements Integration, Closeable {
    /**
     * LISTEN_CALL_STATE
     */
    public static final int LISTEN_CALL_STATE = 0x00000020;
    /**
     * LISTEN_NONE
     */
    public static final int LISTEN_NONE = 0;
    private final @NotNull Context context;
    private SentryOhosOptions sentryOhosOptions;
    private DistributedCallManager distributedCallManager;

    /**
     * PhoneStateChangeListener
     */
    @TestOnly
    public PhoneStateChangeListener listener;

    /**
     * PhoneStateBreadcrumbsIntegration
     *
     * @param context Context
     */
    public PhoneStateBreadcrumbsIntegration(final @NotNull Context context) {
        this.context = Objects.requireNonNull(context, "Context is required");
    }

    @Override
    public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) {
        Objects.requireNonNull(hub, "Hub is required");
        this.sentryOhosOptions =
            Objects.requireNonNull(
                (options instanceof SentryOhosOptions) ? (SentryOhosOptions) options : null,
                "SentryOhosOptions is required");

        this.sentryOhosOptions
            .getLogger()
            .log(
                SentryLevel.DEBUG,
                "enableSystemEventBreadcrumbs enabled: %s",
                this.sentryOhosOptions.isEnableSystemEventBreadcrumbs());

        if (this.sentryOhosOptions.isEnableSystemEventBreadcrumbs()
            && Permissions.hasPermission(context, "ohos.permission.READ_CONTACTS")) {
            distributedCallManager = DistributedCallManager.getInstance(context);

            if (distributedCallManager != null) {
                try {
                    listener = new PhoneStateChangeListener(hub);
                    distributedCallManager.addObserver(listener, LISTEN_CALL_STATE);

                    options.getLogger().log(SentryLevel.DEBUG, "PhoneStateBreadcrumbsIntegration installed.");
                } catch (Exception e) {
                    this.sentryOhosOptions
                        .getLogger()
                        .log(SentryLevel.INFO, e, "TelephonyManager is not available or ready to use.");
                }
            } else {
                this.sentryOhosOptions.getLogger().log(SentryLevel.INFO, "TelephonyManager is not available");
            }
        }
    }

    @Override
    public void close() throws IOException {
        if (distributedCallManager != null && listener != null) {
            distributedCallManager.addObserver(listener, LISTEN_NONE);
            listener = null;

            if (sentryOhosOptions != null) {
                sentryOhosOptions.getLogger().log(SentryLevel.DEBUG, "PhoneStateBreadcrumbsIntegration removed.");
            }
        }
    }

    /**
     * PhoneStateChangeListener
     *
     * @since 2021-06-01
     */
    public static final class PhoneStateChangeListener extends CallStateObserver {
        private final @NotNull IHub hub;

        PhoneStateChangeListener(final @NotNull IHub hub) {
            this.hub = hub;
        }

        @Override
        public void onCallStateUpdated(int state, String number) {
            // incomingNumber is never used and it's always empty if you don't have permission:
            if (state == CALL_STATE_RINGING) {
                final Breadcrumb breadcrumb = new Breadcrumb();
                breadcrumb.setType("system");
                breadcrumb.setCategory("device.event");
                breadcrumb.setData("action", "CALL_STATE_RINGING");
                breadcrumb.setMessage("Device ringing");
                breadcrumb.setLevel(SentryLevel.INFO);
                hub.addBreadcrumb(breadcrumb);
            }
        }
    }
}
